
import java.net.Socket;
import java.util.Arrays;
import java.util.Random;

import javax.swing.JOptionPane;


public class Game
{
	private SocketServer server;
	private Player[] players;
	private int plNr;
	private Deck deck ;
	private int startCoins;
	private int whoDealer;
	private int whoseTurn;
	private int playerWithSmallBlind;
	private int stake = 0; // aktualna stawka
	private int round = 1;
	private int smallBlind;
	private int bigBlind;
	private int maxBet;
	private int moveCounter = 1; // liczba wykonanych ruchów w rundzie
	private int playersExchangedCardsCounter  = 0;
	private int playersFoldedCounter = 0;
	private int playersInGameCounter;
	private int playersBeingInGameInActualRound;
	private int playersClosedResults = 0;
	private boolean isBet = false;
	int sitingPlayersCounter = 0  ; // aktualna liczba siedzacych graczy

	public Game(int playersNr, int startCoins, int port, String name, int smallBlind, int bigBlind )
	{
		this.plNr =playersNr;
		this.players = new Player[playersNr+1];
		this.startCoins = startCoins;
		this.server = new SocketServer(port, playersNr, this);
		this.smallBlind = smallBlind;
		this.bigBlind = bigBlind;
		server.start();
		this.deck = new Deck();
		this.playersInGameCounter = playersNr;
		this.playersBeingInGameInActualRound = playersNr;
	}
	public void sitPlayer(Socket client){
		sitingPlayersCounter++;
		players[sitingPlayersCounter] = new Player(sitingPlayersCounter, client,startCoins,this, deck);
		players[sitingPlayersCounter].start();
	}
	public void startGame(){

		whoDealer = Croupier.drawDealer(plNr);
		System.out.println("whoDealer:"+whoDealer);
		setPlayersWithBlinds();
		whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
		for(int i=1; i<players.length; i++)
			players[i].displayCards();
		enableButtons(players[whoseTurn], isBet);
	}
	public void setPlayersWithBlinds(){
		playerWithSmallBlind = Croupier.whichPlayerNext2(whoDealer, players,false);
		while(players[playerWithSmallBlind].getPlayersCoins()<smallBlind){
			updateFold(playerWithSmallBlind);
			playerWithSmallBlind = Croupier.whichPlayerNext2(whoDealer, players,false);
		}
		playerWithSmallBlind = Croupier.whichPlayerNext2(whoDealer, players,false);
		players[playerWithSmallBlind].setBet(smallBlind);
		displaySmallBlind(players[playerWithSmallBlind].getNick(),smallBlind);
		updateCoins(players[playerWithSmallBlind],0-smallBlind);
		int playerWithBigBlind = Croupier.whichPlayerNext2(playerWithSmallBlind, players, false);
		while(players[playerWithBigBlind].getPlayersCoins()<bigBlind){
			updateFold(playerWithBigBlind);
			playerWithBigBlind = Croupier.whichPlayerNext2(playerWithSmallBlind, players,false);
		}
		players[playerWithBigBlind].setBet(bigBlind);
		maxBet=bigBlind;
		whoseTurn = playerWithBigBlind;
		displayBigBlind(players[playerWithBigBlind].getNick(),bigBlind);
		updateCoins(players[playerWithBigBlind],0-bigBlind);
		updateStake(smallBlind+bigBlind);
	}

	public int getPlayersNumber(){
		return (players.length-1);
	}
	/*
	 * Method returns formated String, which is create in this way:
	 * <player1name><player2name>...
	 * and set name of newPlayer in previous players frames
	 */
	public String introduceNewPlayer(String name){
		String names = "";
		for(int j=sitingPlayersCounter-1; j>0; j--){
			names = names + players[j].getNick();
			players[sitingPlayersCounter-j].addNewPlayer(name);
		}
		return names;
	}
	public void displayMessage(String message){
		for(int j=1; j<=sitingPlayersCounter;j++){
			players[j].displayMessage(message);
		}
	}
	public void displayCoins(int amount, String name){
		String am = Integer.toString(amount);
		for(int i=1; i<players.length; i++){
			players[i].displayCoins(am, name);
		}
	}
    private String getWinners() {
        String winners = "";
        int[][] Hands = new int[players.length][];

        for(int i = 1; i < players.length; i++) {
            Hands[i] = Hand.getReducedHand(players[i].getPlayerHand().getCards());
        }
        int[] bestHand = {-1,-1,-1,-1};
        for(int i = 1; i < players.length; i++) {
            if(players[i].getStatus() != Player.PLAYER_OUT_OF_GAME) {
                if(bestHand[0] != -1)
                    bestHand = Hand.giveBetterHand(bestHand, Hands[i]);
                else
                    bestHand = Hands[i];
            }
        }
        for(int i = 1; i < players.length; i++) {
            if(players[i].getStatus() == Player.PLAYER_OUT_OF_GAME){
                winners += "L";
            }
            else {
                if (!(bestHand.equals(Hands[i]))) {
                    if (!(Hand.giveBetterHand(Hands[i], bestHand).equals(Hand.giveBetterHand(bestHand, Hands[i])))) {
                        winners += "W";
                    } else
                        winners += "L";
                } else
                    winners += "W";
            }
        }
        System.out.println(winners);
        return winners;

    }
    public void displayWinner() {
        String message = "";
        for(int i = 1; i < players.length; i++) {
            if(players[i].getStatus() == Player.PLAYER_OUT_OF_GAME) {
                message += players[i].getNick() + "FOLDED**"; //must be 8 characters
            }
            else {
                message += players[i].getNick() + players[i].getPlayerHand().getString();
            }
        }
        message += getWinners();
        for(int i=1; i<players.length; i++){
            players[i].displayWinner(message);
        }
        System.out.println("displayeWinner:" + message);
    }
    public void givePlayerTheirPrize() {
        String winnersString = getWinners();
        int numberOfWinners = 0;
        for(int i = 0; i < winnersString.length(); i++) {
            if(Character.toString(winnersString.charAt(i)).equals("W")) {
                numberOfWinners++;
            }
        }
        if (numberOfWinners == 1) { //Only one winner. Bank doesn't take a prize
            for(int i = 0; i < winnersString.length(); i++) {
                if(Character.toString(winnersString.charAt(i)).equals("W")) {
                    if(players[i+1].getStatus() != Player.PLAYER_PLAYED_ALLIN) { //regular winner (NOT ALL IN)
                        updateCoins(players[i+1], stake);
                    }
                    else { //player[i] played ALL IN
                        int prize = 0;
                        int allInBet = players[i+1].getBet();
                        for (int j = 1; j < players.length; j++) {
                            if(allInBet <= players[i+1].getBet())
                                prize += allInBet;
                            else
                                prize += players[i+1].getBet();
                        }
                        updateCoins(players[i+1], prize);
                    }
                }
            }
        }

    }
	public void updateCoins(Player player, int change){
		int newCoins = player.getPlayersCoins() + change;
		player.setPlayersCoins(newCoins);
		String name = player.getNick();
		String s = Integer.toString(newCoins);
		for(int i=1; i<players.length; i++){
			players[i].displayCoins(s, name);
		}
	}
	public void updateBet(int playerNr ,String bet){
		isBet = true;
		int temp = Integer.parseInt(bet);
		int sum = temp + players[playerNr].getBet();
		if (sum > maxBet) maxBet = sum;
		String s = Integer.toString(sum);
		players[playerNr].setBet(sum);
		updateCoins(players[playerNr],0-temp);
		updateStake(temp);
		for(int i=1; i<players.length; i++){
				players[i].displayBet(s, players[playerNr].getNick());
		}
		nextPlayerMove();
	}
	public void displayPlayersHands() {
        String[] playerHands = new String[6];

    }
	public void displayBigBlind(String name, int bBlind){
		String bB = Integer.toString(bBlind);
		for(int i=1; i<players.length; i++){
				players[i].displayBigBlind(name, bB);
		}
	}
	public void displaySmallBlind(String name, int sBlind){
		String sB = Integer.toString(sBlind);
		for(int i=1; i<players.length; i++){
				players[i].displaySmallBlind(name, sB);
		}
	}
	public void displayNotes(String name,int status){
		if(status==Player.PLAYER_PLAYED_ALLIN){
			for(int i=1; i<players.length; i++){
				players[i].displayAllin(name);
			}
		}
		else{
			for(int i=1; i<players.length; i++){
				players[i].displayFold(name);
			}
		}
	}
	public void updateStake(int betChange){
		stake = stake + betChange;
		for(int i=1; i<players.length; i++){
			players[i].displayStake(Integer.toString(stake));
		}
		
	}
	public void nextPlayerMove(){
		if( playersFoldedCounter<(plNr-1) ){ 
			if(playersBeingInGameInActualRound>1){ // chociaż dwóch graczy nie dalo fold ani all-in
				whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
		        //gdy wszyscy wymienili karty
		        if(playersExchangedCardsCounter==(plNr-playersFoldedCounter) ){
		                updateRound();
		        }
		        //runda pierwsza i nikt przed osobą z dużą ciemną nie przebił duzej ciemnej
		        if(moveCounter==plNr-1 && round==1 && Croupier.havePlayersTheSameBets(players)){
		        	enableButtons(players[whoseTurn], isBet);
		            players[whoseTurn].enableButtons("H"); //dodatkowo może dać check
		        	moveCounter++;
		        }//POCZATEK WYMIANY wszyscy gracze wykonali mogli wykonać ruch i gracze w grze mają te same zaklady
		        else if (moveCounter>=playersBeingInGameInActualRound && Croupier.havePlayersTheSameBets(players)){
		        	if(round<4){
		        		while(players[whoseTurn].getStatus()==Player.PLAYER_OUT_OF_GAME){
		        			whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
		        		}
		        		enableCardsExchange(players[whoseTurn]);
		        		playersExchangedCardsCounter++;
		        	}
		        	else{ //KONIEC GRY po 4 rundach- przynajmniej dwóch graczy nie dało fold ani all-in
                        displayWinner();
                        givePlayerTheirPrize();
                        prepareNewDeal();
		        		System.out.println("Koniec pierwszego rozdania");      
		        	}
		        }
		        else{ //Kontynuacja ruchów- rózne zakłady lub nie wszyscy mogli wykonać ruch
		        	while(players[whoseTurn].getStatus()!=Player.PLAYER_IN_GAME){
		        		whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
		            }
		            enableButtons(players[whoseTurn], isBet);
		            moveCounter++;
		        }
			}//jest tylko jeden gracz, który nie dał fold ani all-in, i chociaż jeden z all-in
			else{//wszyscy wymienili karty
				if(playersExchangedCardsCounter==(plNr-playersFoldedCounter) ){
	                updateRound();
				}
				else{//trwa wymiana kart
					if(round<4){
		        		while(players[whoseTurn].getStatus()==Player.PLAYER_OUT_OF_GAME)
		        			whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
		        		enableCardsExchange(players[whoseTurn]);
		        		playersExchangedCardsCounter++;
		        	}
		        	else{//KONIEC GRY
                        displayWinner();
                        givePlayerTheirPrize();
                        prepareNewDeal();
		        		System.out.println("Koniec pierwszego rozdania");     
		        	}
				}
			}
		}
		else{//Koniec GRY został tylko jeden gracz w grze(zwyciezca), reszta dała fold
            displayWinner();
            System.out.println("stake:" +stake);
            givePlayerTheirPrize();
            for(int i=1; i<players.length; i++){
            	System.out.println(players[i].getNick()+ " ->" + players[i].getPlayersCoins());
            }
            prepareNewDeal();
			System.out.println("Koniec pierwszego rozdania");
		}
	}

	public void updateAllin(int playerNr){
		players[playerNr].setStatus(Player.PLAYER_PLAYED_ALLIN);
		String s = Integer.toString(players[playerNr].getPlayersCoins());
		playersInGameCounter--;
		updateBet(playerNr,s);
		displayNotes(players[playerNr].getNick(), Player.PLAYER_PLAYED_ALLIN);
		
	}
	public void updateFold(int playerNr){
		players[playerNr].setStatus(Player.PLAYER_OUT_OF_GAME);
		displayNotes(players[playerNr].getNick(), Player.PLAYER_OUT_OF_GAME);
		playersInGameCounter--;
		playersFoldedCounter++;
		nextPlayerMove();
		
	}
	/*
	 * Method enable  pressing the buttons appropriately to the player state
	 * H-Check,B-Bet,R-Raise,C-Call,F-Fold,A-Allin,E-Exchange
	 */
	public void enableButtons(Player player, boolean isBet){
		int bet = player.getBet();
		int coins = player.getPlayersCoins();
		player.enableButtons(Croupier.possibleMoves(bet, coins, isBet, maxBet, round));
	}
	private void enableCardsExchange(Player player){
		player.enableButtons("E");
	}
	public void exchangeCards(int PlayerNr, String binaryCardSet){
		int cardSet = Integer.parseInt(binaryCardSet, 2);
		if(cardSet>0){
			int exchangedCardsCounter = 0;
			for(int i=0; i<Hand.HAND_SIZE; i++){
				if(binaryCardSet.charAt(i)=='1')
					exchangedCardsCounter++;
			}
			players[PlayerNr].exchangeCards(deck, cardSet);
			players[PlayerNr].displayCards();
			for(int i = 1; i<players.length; i++){
				if (i!=PlayerNr) players[i].displayAmountOfExchanged(players[PlayerNr].getNick(),Integer.toString(exchangedCardsCounter));
			}
		}
		nextPlayerMove();
	}
	public void updateRound(){
		round++;
		moveCounter = 0;
		isBet = false;
		playersExchangedCardsCounter = 0;
		playersBeingInGameInActualRound = playersInGameCounter;
		whoseTurn = playerWithSmallBlind;
		for(int i=1; i<players.length; i++){
			players[i].displayRound(Integer.toString(round));
		}
	}
	public void prepareNewDeal(){
		int playersNotWorkingCounter = 0;
		for(int i=1; i<players.length; i++){//chyba trzeba zwrócić karty gracza odłaczonego
			players[i].getPlayerHand().discardAllCards(deck);
			players[i].setBet(0);
			if(players[i].isWorking() && players[i].getPlayersCoins()>0){
				players[i].setStatus(Player.PLAYER_IN_GAME);
			}
			else {
				playersNotWorkingCounter++;
				deletePlayer(i);
			}
		}
		System.out.println("shuffle");
		deck.shuffle();
		System.out.println("po shuffle");
		for(int i=1; i<players.length; i++){
			System.out.println("isWorking" + i + "->" + players[i].isWorking());
			if(players[i].isWorking() &&players[i].getPlayersCoins()>0){
				players[i].getPlayerHand().drawAllCards(deck, i);
				for(int j=1; j<players.length; j++){
					players[j].displayBet("0", players[i].getNick());
				}
			}
		}
		if( (plNr-playersNotWorkingCounter)<2){
			JOptionPane.showMessageDialog(null, "All player has gone except you" ,
					"Error", JOptionPane.ERROR_MESSAGE);
			System.exit(1);
		}
		else{
			round = 1;
			stake = 0;
			isBet = false;
			playersFoldedCounter = playersNotWorkingCounter;
			playersInGameCounter = plNr-playersNotWorkingCounter;
			playersExchangedCardsCounter = 0;
			//playersBeingInGameInActualRound = playersInGameCounter;
			whoDealer = Croupier.whichPlayerNext2(whoDealer, players, false);
			for(int i=1; i<players.length; i++){
				players[i].displayRound(Integer.toString(round));
			}
		}
	}
	public void startNewDeal(){// nie mogłam znaleźc jak jest rozgrywka ;P
		setPlayersWithBlinds();
		playersBeingInGameInActualRound = playersInGameCounter;
		whoseTurn = Croupier.whichPlayerNext2(whoseTurn, players,false);
		for(int i=1; i<players.length; i++)
			if(players[i].isWorking() &&players[i].getPlayersCoins()>0){
				players[i].displayCards();
			}
		moveCounter = 1;
		enableButtons(players[whoseTurn], isBet);		
	}
	public void deletePlayer(int number){
		for(int i=1; i<players.length; i++){
			if(i!=number){
				players[i].deletePlayer(players[number].getNick());
			}
		}
	}
	public boolean isNameFree(String name){
		for(int i=1; i<=sitingPlayersCounter; i++){
				if(name.equals(players[i].getNick()))	return false;
		}
		return true;
	}
	public void closeResults(int playerNr){
		if (players[playerNr].getStatus()!=Player.PLAYER_OUT_OF_GAME) playersClosedResults++;
		if (playersClosedResults == playersInGameCounter){
			startNewDeal();
			playersClosedResults = 0;
		}
	}
}
